home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / net / bind-contrib.tar.gz / bind-contrib.tar / contrib / misc / dnsfind.shar / dnsfind.pl next >
Encoding:
Text File  |  1996-10-25  |  6.5 KB  |  328 lines

  1. # dnsfind - like find(1), but for DNS data
  2. #
  3. # $Id: dnsfind.shar,v 8.2 1996/10/25 17:07:55 vixie Exp $
  4. # $Source: /proj/src/isc/cvs-1/bind/contrib/misc/dnsfind.shar,v $
  5. #
  6. # SYNOPSIS
  7. #    #!/usr/local/bin/perl
  8. #    require 'dnsfind.pl';
  9. #    &dnsfind ('my.domain.com', '1.128.in-addr.arpa');
  10. #    sub dnswanted {
  11. #        printf ("%40s %10s %s\n", $zone, $type, $value);
  12. #    }
  13. #
  14. # DESCRIPTION
  15. #    Recursively seaches DNS zones, and calls the user-defined
  16. #    subroutine "dnswanted" for each record found.  Fields from the
  17. #    record are put in the following global variables:
  18. #
  19. #        $parent_zone    The parent zone which is being
  20. #                examined via a "dig axfr" (zone
  21. #                transfer) command.
  22. #        $server        The canonical name of the primary
  23. #                server for this (parent) zone.  From
  24. #                the SOA record.
  25. #        $zone        Fully-qualified domain-name.
  26. #        $timeout        Seconds before record is out of date.
  27. #        $type        Record type (A, MX, CNAME, NS, etc).
  28. #        $value        Value of the record.
  29.  
  30. #
  31. # Safe return from "require"
  32. #
  33. 1;
  34.  
  35. ##############################################################################
  36. # dnsfind
  37. #
  38. sub dnsfind {
  39.     local (@zones) = @_;
  40. #   GLOBAL ($parent_zone)
  41. #   GLOBAL ($server)
  42. #   GLOBAL ($zone)
  43. #   GLOBAL ($timeout)
  44. #   GLOBAL ($type)
  45. #   GLOBAL ($value)
  46. #   GLOBAL (%_zones_searched)
  47.  
  48.     local ($parent_zone_l);
  49.     local ($server_l);
  50.     local ($zone_l);
  51.     local ($timeout_l);
  52.     local ($type_l);
  53.     local ($value_l);
  54.     local (@sub_zones);
  55.     local (@data);
  56.     local ($record);
  57.     local ($tmp);
  58.  
  59.     foreach $parent_zone_l (@zones) {
  60.     #
  61.     # Find primary
  62.     #
  63.     if (substr ($parent_zone_l, -1) ne '.') {
  64.         $parent_zone_l .= '.';
  65.     }
  66.     @data = &dig ($parent_zone_l, 'soa');
  67.     if (! @data) {
  68.         return undef;
  69.     }
  70.     @data = split (/\s+/, shift (@data));
  71.     $server_l = shift (@data);
  72.  
  73.     #
  74.     # Avoid this zone if we have already searched it
  75.     #
  76.     $tmp = $parent_zone_l;
  77.     $tmp =~ tr/[A-Z]/[a-z]/;
  78.     next if ($_zones_searched{$tmp});
  79.  
  80.     #
  81.     # Transfer zone
  82.     #
  83.     ###print ("[AXFR] $parent_zone_l\n");
  84.     @data = &axfr ($parent_zone_l, $server_l);
  85.     if (! @data) {
  86.         return undef;
  87.     }
  88.  
  89.     #
  90.     # Note this zone as having been searched
  91.     #
  92.     $tmp = $parent_zone_l;
  93.     $tmp =~ tr/[A-Z]/[a-z]/;
  94.     $_zones_searched{$tmp} = 1;
  95.  
  96.     #
  97.     # Process each record
  98.     #
  99.     foreach $record (@data) {
  100.         ($zone_l, $timeout_l, $type_l, $value_l) = split ('\s+', $record,
  101.                                   4);
  102.         #
  103.         # We only want data in this zone
  104.         #
  105.         next if (&strcasecmp ($parent_zone_l,
  106.                   substr ($zone_l, 0 - length ($parent_zone_l)))
  107.              != 0);
  108.  
  109.         #
  110.         # Call the user-supplied subroutine
  111.         #
  112.         $parent_zone = $parent_zone_l;
  113.         $server = $server_l;
  114.         $zone = $zone_l;
  115.         $timeout = $timeout_l;
  116.         $type = $type_l;
  117.         $value = $value_l;
  118.  
  119.         &dnswanted ();
  120.  
  121.         #
  122.         # Is it a sub-zone, and have we not searched it?
  123.         #
  124.         next if (&strcasecmp ($type_l, 'NS') != 0);
  125.         next if (! &is_sub_zone ($zone_l, $parent_zone_l));
  126.         next if (grep ($_ eq $zone_l, @sub_zones));
  127.         #
  128.         # Save it to search later
  129.         #
  130.         push (@sub_zones, $zone_l);
  131.     }
  132.  
  133.     #
  134.     # Recurse (depth-first) for sub-zones
  135.     #
  136.     &dnsfind (sort (@sub_zones))
  137.     }
  138.     return 1;
  139. }
  140.  
  141.  
  142. ##############################################################################
  143. # is_sub_zone
  144. #
  145. sub is_sub_zone {
  146.     local ($zone) = shift (@_);
  147.     local ($parent_zone) = shift (@_);
  148.  
  149.     #
  150.     # To be a sub-zone, you should have a dot, then the parent zone as
  151.     # a suffix.
  152.     #
  153.  
  154.     $parent_zone = '.' . $parent_zone;
  155.  
  156.     if (length ($zone) <= length ($parent_zone)) {
  157.     return 0;
  158.     }
  159.  
  160.     $parent_zone =~ tr/[A-Z]/[a-z]/;
  161.     $zone =~ tr/[A-Z]/[a-z]/;
  162.  
  163.     if (substr ($zone, 0 - length ($parent_zone)) ne $parent_zone) {
  164.     return 0;
  165.     }
  166.  
  167.     return 1;
  168. }
  169.  
  170.  
  171. ##############################################################################
  172. #
  173. sub axfr {
  174.     local ($zone) = shift (@_);
  175.     local ($server) = shift (@_);
  176.  
  177.     local (@output);
  178.     local (@temp);
  179.     local ($_);
  180.     local ($cont);
  181.  
  182.     @output = split (/\n/, `dig @$server axfr $zone`);
  183.  
  184.     if (! &dig_check ($?, @output)) {
  185.     return ();
  186.     }
  187.  
  188.     #
  189.     # Remove comments and newlines
  190.     #
  191.     @temp = ();
  192.     foreach (@output) {
  193.     s/\s*\n$//;
  194.     s/\s*;.*$//;
  195.     if (! /^\s*$/) {
  196.         push (@temp, $_);
  197.     }
  198.     }
  199.  
  200.     #
  201.     # Collapse continuations
  202.     #
  203.     @output = ();
  204.     while (1) {
  205.     last if (! @temp);
  206.     $_ = shift (@temp);
  207.     while (/\s\($/ && @temp) {
  208.         s/\s*\($//;
  209.         $cont = shift (@temp);
  210.         if ($cont =~ /\)$/) {
  211.         $cont =~ s/\s*\)$//;
  212.         $_ = $_ . ' ' . $cont;
  213.         } else {
  214.         $_ = $_ . ' ' . $cont . ' (';
  215.         }
  216.     }
  217.     push (@output, $_);
  218.     }
  219.  
  220.     return (@output);
  221. }
  222.  
  223.  
  224. ##############################################################################
  225. # dig -    Simple DNS lookup, using dig(1).
  226. #
  227. sub dig {
  228.     local ($key) = shift (@_);
  229.     local ($type) = shift (@_);
  230.     local ($server) = shift (@_);
  231.  
  232.     local (@output);
  233.     local ($_);
  234.     local (%values);
  235.     local ($value);
  236.     local (@F);
  237.     local (@ret);
  238.     local ($status);
  239.  
  240.     #
  241.     # Key must be relative to root
  242.     #
  243.     if (substr ($key, -1) ne '.') {
  244.     $key .= '.';
  245.     }
  246.  
  247.     if (length ($server)) {
  248.         @output = split (/\n/, `dig @$server $key $type`);
  249.     } else {
  250.     @output = split (/\n/, `dig $key $type`);
  251.     }
  252.  
  253.     if (! &dig_check ($?, @output)) {
  254.     return ();
  255.     }
  256.  
  257.     #
  258.     # Find matching data
  259.     #
  260.     undef %values;
  261.     foreach (@output) {
  262.     next if (/^;/);
  263.     next if (/^\s*$/);
  264.     @F = split;
  265.     next if (&strcasecmp ($type, $F[2]) != 0);
  266.     next if (&strcasecmp ($key, $F[0]) != 0);
  267.     shift (@F); shift (@F); shift (@F);
  268.     $value = join (' ', @F);
  269.     next if ($values{$value});
  270.     $values{$value} = 1;
  271.     push (@ret, $value);
  272.     }
  273.     return @ret;
  274. }
  275.  
  276.  
  277. ##############################################################################
  278. # dig_check -    Check how dig(1) went
  279. #
  280. sub dig_check {
  281.     local ($status) = shift (@_);
  282.  
  283.     local (@F);
  284.     local ($status_text);
  285.  
  286.     if (! @_) {
  287.     #
  288.     # dig has probably not executed
  289.     #
  290.     $ERROR_STR = "problem running dig?";
  291.     return ();
  292.     }
  293.  
  294.     #
  295.     # Get status text, regardless of return value
  296.     #
  297.     @F = grep (/;; ->>HEADER<<-/, @_);
  298.     if (@F) {
  299.     if ($F[0] =~ /status: ([^,]*),/) {
  300.         $status_text = $1;
  301.     }
  302.     }
  303.     if (($status_text eq "REFUSED") || ($status_text eq "SERVFAIL")) {
  304.     $ERROR_STR = "dig: status: $1";
  305.     return ();
  306.     }
  307.     if ($? != 0) {
  308.     $ERROR_STR = "dig: error, ret = " . ($? >> 8) . ", status = $status";
  309.     return ();
  310.     }
  311.  
  312.     return 1;
  313. }
  314.  
  315.  
  316. ##############################################################################
  317. # strcasecmp -    Compare strings while ignoring case
  318. #
  319. sub strcasecmp {
  320.     local ($s1) = shift (@_);
  321.     local ($s2) = shift (@_);
  322.  
  323.     $s1 =~ tr/[A-Z]/[a-z]/;
  324.     $s2 =~ tr/[A-Z]/[a-z]/;
  325.  
  326.     return ($s1 cmp $s2);
  327. }
  328.